package demos;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

class ThreadList<T> {
	private volatile LinkedList<T> list;
	private Object syncObj = new Object();
	
	public ThreadList() {
		list = new LinkedList<>();
	}
	
	public void add(T item) {
		synchronized (syncObj) {
			list.add(item);
			syncObj.notifyAll();
		}
	}
	
	public T take() {
		synchronized (syncObj) {
			try {
				while(list.size() == 0)
					syncObj.wait();
			} catch (InterruptedException e) {
				//e.printStackTrace();
			}
			return list.removeFirst();
		}
	}
}

class NumberGenerator implements Runnable {
	private ThreadList<Integer> list;
	
	public NumberGenerator(ThreadList<Integer> list) {
		this.list = list;
	}
	
	@Override
	public void run() {
		int i = 0;
		try {
			while(!Thread.interrupted()) {
				list.add(i);
				i++;
				TimeUnit.MILLISECONDS.sleep((long) (Math.random() * 500));
			}
		} catch (InterruptedException e) {
			//e.printStackTrace();
		}
	}
}

class Calculator implements Runnable {
	ThreadList<Integer> aList;
	ThreadList<Integer> bList;
	
	public Calculator(ThreadList<Integer> aList, ThreadList<Integer> bList) {
		this.aList = aList;
		this.bList = bList;
	}
	
	@Override
	public void run() {
		try {
			while(!Thread.interrupted()) {
				synchronized (aList) {
					int a = aList.take();
					int b = bList.take();
					System.out.print(Thread.currentThread().getName() + ": ");
					System.out.println(a + " * " + b + " = " + (a * b));
				}
				TimeUnit.MILLISECONDS.sleep(200);
				
			}
		} catch (Exception e) {
		}
	}
}

public class CalculateThread {
	public static void main(String[] args) throws InterruptedException {
		ThreadList<Integer> aList = new ThreadList<>();
		ThreadList<Integer> bList = new ThreadList<>();
		ExecutorService exec = Executors.newCachedThreadPool();
		exec.execute(new Calculator(aList, bList));
		exec.execute(new Calculator(aList, bList));
		exec.execute(new NumberGenerator(aList));
		exec.execute(new NumberGenerator(bList));
		TimeUnit.SECONDS.sleep(40);
		exec.shutdownNow();
	}
}
